﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Demo.Framework.Datastructures
{
    // a->b->c->d
    public class LinkedList<T>
    {
        public Node<T> GetHead()
        {
            return _head;
        }

        public Node<T> GetTail()
        {
            var current = _head;
            while (current!=null)
            {
                current = current.Next;
            }
            return current;
        }

        private Node<T> _head;
                
        /// <summary>
        /// Gets the count of the number of elements
        /// </summary>
        public int Count
        {
            get 
            {
                if (_head == null)
                {
                    return 0;
                }

                Node<T> element = _head;
                int count = 0;
                do
                {
                    element = element.Next;
                    count++;
                    
                } while (element !=null);

                return count;
            }
        }

        public LinkedList()
        {
            _head = null;
        }
        
        /// <summary>
        /// Inserts a Node in Nth position
        /// </summary>
        /// <param name="position"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Insert(int position, T value)
        {
            try
            {
                Node<T> newNode = new Node<T>(value);

                if (position < 0) { throw new Exception("Invalid Position"); }
                
                if (IsEmpty())
                {
                    _head = newNode;
                }
                else
                {
                    if (position == 0)
                    {
                        // insert it as head                        
                        newNode.Next = _head;
                        _head = newNode;
                    }
                    else
                    {
                        Node<T> current = _head;
                        for (int i = 0; i < position -1 && current !=null; i++)
                        {
                            current = current.Next;
                        }

                        if (current == null)
                            throw new Exception("Invalid Input : Position cannot be greater than the size of collection");

                        newNode.Next = current.Next == null ? null : current.Next;                        
                        current.Next = newNode;                        
                    }
                }

                return true;                
            }
            catch (Exception)
            {                
                return false;
            }
        }        

        public bool Remove(T data)
        {
            try
            {
                Node<T> currentNode = _head;
                if (IsEmpty()) { throw new Exception("The linked list is already empty"); }

                if (_head.Value.Equals(data))
                {
                    _head = _head.Next == null ? null : _head.Next;
                    return true;
                }

                while (currentNode.Next != null)
                {
                    if (currentNode.Next.Value.Equals(data))
                    {
                        currentNode.Next = currentNode.Next.Next == null ? null : currentNode.Next.Next;
                        return true;
                    }
                    else
                    {
                        currentNode = currentNode.Next;
                    }
                }

                return false;
            }
            catch (Exception)
            {                
                return false;
            }
        }

        /// <summary>
        /// Remove a Node from a position
        /// </summary>
        /// <param name="position"></param>
        /// <returns></returns>
        public T Remove(int position)
        {
            Node<T> deletedItem;

            if (IsEmpty())
            {
                // remove the head
                throw new Exception("The collection is already empty");
            }
            else
            {
                if (position == 0)
                {
                    // remove the head;
                    deletedItem = _head;
                    _head = _head.Next;
                }
                else
                {
                    Node<T> current = _head;

                    for (int i = 0; i < position - 1 && current != null; i++)
                    {
                        current = current.Next;
                    }

                    deletedItem = current.Next;
                    current.Next = current.Next.Next == null ? null : current.Next.Next;
                }
            }

            return deletedItem.Value;   
        }

        /// <summary>
        /// Update data of a node
        /// </summary>
        /// <param name="position"></param>
        /// <param name="newData"></param>
        /// <returns></returns>
        public bool UpdateNode(int position, T newData)
        {
            try
            {
                if (IsEmpty() || position < 0) { throw new Exception("Invalid Position"); }
                if (position == 0)
                {
                    _head.Value = newData;
                    return true;
                }
                else
                {
                    Node<T> current = _head;
                    for (int i = 0; i < position && current !=null; i++)
                    {
                        current = current.Next;
                    }
                    if (current == null) { throw new Exception("Invalid Position"); }

                    current.Value = newData;
                    return true;
                }
            }
            catch (Exception ex)
            {                
                Console.WriteLine(ex.Message);
                return false;
            }
            
        }
                
        public void Clear()
        {
            _head = null;
        }       
                        
        public override string ToString()
        {
            int count = Count;
            StringBuilder sb = new StringBuilder();
            Node<T> nodeElement = _head;
            for (int i = 0; i < count; i++)
            {
                sb.Append(nodeElement.ToString());
                if (i != count - 1)
                {
                    sb.Append(",");
                }
                nodeElement = nodeElement.Next;
            }

            return sb.ToString();
        } 

        public bool IsEmpty()
        {
            return _head == null;
        }

    }
}
